/* 3/4 -- CHANGED!!!
 * made changed to PPRule and free_Rule
 * copied the rule buffer to solve a problem with data managing for rainbow table
 * so i needed to free it later  ------ NEEDS TESTING
 *
 * tests are GOOD !!!!!1
 */

#include "generator.h"
#include "misc.h"
#include <math.h>

/*main generating function for passwords*/
int generator(LONG_INDEX_PROJ k, Rule* ruleData, char* buffer){
	char **block;
	LONG_INDEX_PROJ *range;
	LONG_INDEX_PROJ lastrange;
	Dict* dictData = NULL;
	int i=0;
	dictData = &(ruleData->dict);

	block=ruleData->block;
	range=ruleData->blockRange;
	lastrange=0;

	k = k % ruleData->blockRange[ruleData->numBlocks-1];

	for (i=0; ((i< ruleData->numBlocks) && (k>=ruleData->blockRange[i]) ) ; i++){
	lastrange=ruleData->blockRange[i];
	}

	k=k-lastrange;
	return K_to_password_subrule(k, ruleData->block[i], buffer, dictData);
}

/*this is a comb. function calculating size of a segment (i.e. ?1-3)*/
long HowManyOptionsForSagment(int min, int max, char type, Dict* dictData){
	LONG_INDEX_PROJ size=0;

	if ((type=='@') || (type=='#') || (type=='$')) return SizeOfDictionary(type,dictData);
	else 
	{
		if (max==min) return pow(SizeOfAlfabet(type,dictData),min);
		if (max>min)
		{
			while (max>=min)
			{
				size+=pow(SizeOfAlfabet(type,dictData),max);
				max--;
			}
			return size;
		}
		else return -1;
	}
}

/*returns the comb. size of dictionary segment*/
long SizeOfDictionary(char type, Dict* dictData){
	if ((type=='@') || (type=='#'))
		return dictData->numWords;
	if (type=='$')
		return dictData->sumWordsPowered;
	return -1;
}

int SizeOfAlfabet(char type, Dict* dictData){
	if (type=='?') return 94; /* all writeble chars */
	if (type=='&') return 10; /* digits */
	if (type=='!') return 52; /* alphabet lower & upper */
	if (type=='%') return 62; /* alphabet lower & upper + nums */
	if ((type=='@') || (type=='#') || (type=='$')) return SizeOfDictionary(type, dictData);
	else return -1;
}

/* returns the number of chars written 
   working on a sub-rule i.e. !1-2$?3-4*/
int K_to_password_subrule(LONG_INDEX_PROJ k, char* rule, char* buffer, Dict* dictData){
	int min,max,length,lengthtemp;
	LONG_INDEX_PROJ howbig;
	char type;
	char ** Ptr2buffer=&buffer;
	length=0;
	lengthtemp=0;

	while (*rule != '\0')
	{
		min=0;
		max=0;
		/* reading a segment*/
		type = *rule;
		rule+= 1;
		if ( (type=='?') || (type=='&') || (type=='!') || (type=='%') )
		{
			min = atoi(rule);
			while (*rule != '-')
				rule=rule+1;
			max = atoi((rule+= 1));
			while (('0' <= *rule) && (*rule <='9'))
				rule=rule+1;
		}

		/*translating a segment to buffer*/
		howbig = HowManyOptionsForSagment(min, max, type, dictData);

		if ( (type=='?') || (type=='&') || (type=='!') || (type=='%') )
			lengthtemp=generateLetter(min, max, type, (k % howbig) , Ptr2buffer, dictData);
		if ( (type=='@') || (type=='#') || (type=='$') )
			lengthtemp=generateWord(type, (k % howbig) , Ptr2buffer, dictData);

		if (lengthtemp==-1)
		{
			*buffer='\0';
			length=-1;
			break;
		}

		length+=lengthtemp;
		k = k / howbig;
	}
	**Ptr2buffer='\0';

	return length;
}

/* generates a dic word, gets a pointer to the buffer so it can progress it */
int generateWord(char type, LONG_INDEX_PROJ k, char **buffer, Dict* dictData){
	LONG_INDEX_PROJ wordsize;
	char** dictpointer; /* word comb */
	unsigned char *sizepointer; /* word size comb */
	char* wordpointer; /* a word in the dict */
	short found; /* flag */

	found = 0;
	dictpointer = dictData->words;
	sizepointer = dictData->wordSize;

	wordsize=0; /* at first it will save the 2^word or words-counter - acording to the type */

	if ((type=='@') || (type=='#'))
	{
		while ( (*dictpointer != NULL) && (!found) )
		{
			if (0 < k)
			{
				dictpointer++; /* need to cont. to next word */
				k--;
			}
			else	/* found the word */
			{
				found=1;
			}
		}
	}

	if (type=='$')
	{
		while ( (*dictpointer != NULL) && (!found) )
		{
			wordsize = 1 << (*sizepointer) ;

			if (wordsize <= k)
			{
				dictpointer++; /* need to cont. to next word */
				sizepointer++;
				k=k-wordsize;
			}
			else	/* found the word - use remaining bits as CAMEL mask */
			{
				found=1;
			}
		}
	}

	if (found){
		wordpointer = *dictpointer; /* return to the beginning */
		wordsize=0; /* now we'll use it to save the length */

		if (type=='@') /* upper case */
		{
			while (*wordpointer!='\0')
			{
				**buffer=tolower(*wordpointer);
				*buffer=*buffer+1;
				wordpointer++;
				wordsize++;
			}
		}

		if (type=='#') /* upper case */
		{
			while (*wordpointer!='\0')
			{
				**buffer=toupper(*wordpointer);
				*buffer=*buffer+1;
				wordpointer++;
				wordsize++;
			}
		}

		if (type=='$') /* CAMEL back */
		{
			while (*wordpointer!='\0')
			{
				if ((k%2)==0)
					**buffer=tolower(*wordpointer);
				else
					**buffer=toupper(*wordpointer);
				*buffer=*buffer+1;
				wordpointer++;
				wordsize++;
				k= k/2;
			}
		}
		return wordsize;

	} else {
		printf("generateWord: k out of dict bounds \n");
		return -1;
	}
}

/* generates a segment, gets a pointer to the buffer so it can progress it */
/* return the number of chars written or -1 for error */
int generateLetter(int min, int max,char type, LONG_INDEX_PROJ k, char **buffer, Dict* dictData){
	LONG_INDEX_PROJ size;
	int i,index;
	int alphabetsize;
	char letter;

	alphabetsize = SizeOfAlfabet(type,dictData);
	size = pow(alphabetsize,min);

	/* find how many letters, and check input */
	while ( (k>=size) ){
		k=k-size;
		min++;
		size=pow(alphabetsize,min);
	}
	/* now: min == how many letters */
	if (min>max){
		return -1;
	}
	/*generation*/
	for (i=0 ; i<min ; i++){
		index = k % alphabetsize;
		k = k / alphabetsize;
		letter = translate_index_to_char(index,type);

		if (letter=='\0')
		{
			return i;
		}
		**buffer = letter;
		*buffer = *buffer+1;
	}

	return i;
}

char translate_index_to_char(int index, char type){
	if (type=='?') return translate_index_to_char_all(index);
	if (type=='&') return translate_index_to_char_digits(index);
	if (type=='!') return translate_index_to_char_letters(index); 
	if (type=='%') return translate_index_to_char_alphanumeric(index); 
	else return '\0';
}

char translate_index_to_char_all(int index){
	if (index<94) return (char) (index+33);
	else return '\0';
}

char translate_index_to_char_digits(int index){
	if (index==0) return '0';
	if (index==1) return '1';
	if (index==2) return '2';
	if (index==3) return '3';
	if (index==4) return '4';
	if (index==5) return '5';
	if (index==6) return '6';
	if (index==7) return '7';
	if (index==8) return '8';
	if (index==9) return '9';
	else {
		return '\0';
	}
}

char translate_index_to_char_letters(int index){
	if (index<26) return (char) (index+65);
	if (index<52) return (char) (index+71);
	else {
		return '\0';
	}
}

char translate_index_to_char_alphanumeric(int index){
	if (index<10) return translate_index_to_char_digits(index);
	else return translate_index_to_char_letters(index-10);
}

/*calc. the number of combinations for a sub-rule*/
int blockComb(char *block, int blockSize, Dict *dict){
	int range = 0, min = 0, max = 0, res = 0;
	char *p = NULL, type = 'a';

	range = 1;

	p = block;
	while (*p != '\0'){
		type = *p;
		if ( (*p=='?') || (*p == '&') || (*p == '!') || (*p == '%') )
		{
			p++;
			min = atoi(p);
			while (*p != '-') p++;
			p++;
			max = atoi(p);
			while (('0' <= *p) && (*p <='9'))
				p++;
		}
		else {
			min = 0;
			max = 0;
			p++;
		}

		res = HowManyOptionsForSagment(min, max, type, dict);
		if (res == -1) return 0;
		range *= res;
	}

	return range;
}

/*this function gets: a dictionary file name, a rule string and a ruleBuffer (of type Rule)
 * allocates whatever needed and calculates everything
 */
Rule *PPRule(char *filename, char *rule){
	Rule *r = NULL;
	int counter = 0, bCounter = 0, i = 0;
	LONG_INDEX_PROJ rangeComb = 0;
	char *p = NULL;
	char *d = NULL, *ruleC = NULL;

	r = (Rule *)malloc(sizeof(Rule));
	if (r == NULL) return NULL;

	ruleC = (char *)calloc(strlen(rule)+1, sizeof(char));
	if (ruleC == NULL){
		free(r);
		return 0;
	}
	strcpy(ruleC, rule);

	p = ruleC;
	while (*p != '\0'){
		if (*p != '|') {
			bCounter++;
			while ((*p != '\0') && (*p != '|')) p++;
		}
		else p++;
	}

	r->block = (char **)calloc(bCounter, sizeof(char *));
	if (r->block == NULL) {
		free(r);
		free(ruleC);
		return NULL;
	}
	r->blockRange = (LONG_INDEX_PROJ *)calloc(bCounter, sizeof(LONG_INDEX_PROJ));
	if (r->blockRange == NULL){
		free(r->block);
		free(r);
		free(ruleC);
		return NULL;
	}
	r->numBlocks = bCounter;

	p = ruleC;
	for (i = 0; i < bCounter; i++){
		r->block[i] = p;
		while (*p != '\0'){
			if (*p == '|'){
				*p = '\0';
				p++;
				break;
			}
			p++;
		}
	}

	d = load_file(filename);
	if (d == NULL) {
		free(r->blockRange);
		free(r->block);
		free(r);
		free(ruleC);
		return NULL;
	}
	r->dict.fPtr = d;

	p = d;
	counter = 0;
	while (*p != '\0'){
		if (isspace(*p)) {
			while ((*p != '\0') && (isspace(*p))) p++;
			if (*p == '\0') break;
		}
		else {
			counter++;
			while ((*p != '\0') && (!isspace(*p))) p++;
			if (*p == '\0') break;
		}
	}
	r->dict.words = (char **)calloc(counter+1, sizeof(char *));
	if (r->dict.words == NULL){
		free(r->block);
		free(r->blockRange);
		free(r);
		free(ruleC);
		free(d);
		return NULL;
	}
	r->dict.wordSize = (unsigned char *)calloc(counter+1, sizeof(unsigned char));
	if (r->dict.wordSize == NULL){
		free(r->block);
		free(r->blockRange);
		free(r->dict.words);
		free(d);
		free(r);
		free(ruleC);
		return NULL;
	}

	r->dict.numWords = counter+1; /*+ the empty word*/

	p = r->dict.fPtr;
	for (i = 1; i < counter+1; i++){
		while ((*p != '\0') && (isspace(*p)))
			p++;
		r->dict.words[i] = p;
		while ((*p != '\0') && (!isspace(*p)))
			p++;
		if (*p == '\0') break;
		else {
			*p = '\0';
			p++;
		}
	}
	*p = '\0';
	r->dict.words[0] = p;  /*leave an empty word in the dictionary array*/

	r->dict.wordSize[0] = 0; /*the empty word length*/
	for (i = 1; i < counter+1; i++){
		r->dict.wordSize[i] = strlen(r->dict.words[i]);
	}
	r->dict.sumWordsPowered = 0;
	for (i = 0; i < counter+1; i++){
		r->dict.sumWordsPowered += (LONG_INDEX_PROJ)(1 << r->dict.wordSize[i]);
	}

	rangeComb = 0;
	for (i = 0; i < bCounter; i++){
		rangeComb += blockComb(r->block[i], strlen(r->block[i]), &(r->dict));
		r->blockRange[i] = rangeComb;
	}

	return r;
}

void free_PPRule(Rule *r){
	if (r != NULL){
		if (r->dict.wordSize != NULL)
			free(r->dict.wordSize);
		if (r->dict.fPtr != NULL)
			free(r->dict.fPtr);
		if (r->dict.words != NULL)
			free(r->dict.words); 
		if (r->block != NULL){
			if (r->block[0] != NULL)
				free(r->block[0]);
			free(r->block);
		}
		if (r->blockRange != NULL)
			free(r->blockRange);
		free(r);
	}
}

